package com.gitee.wsl.common.ui.ext.color

import androidx.annotation.FloatRange
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.unit.Dp


/** Heuristically determines if the color can be thought of as "dark". */
fun Color.isDark(): Boolean = (luminance() + 0.05) / 0.05 < 4.5

@Stable
@Composable
@ReadOnlyComposable
fun Color.alwaysLight(isAlways: Boolean, isInDark:Boolean): Color {
    val colorScheme = MaterialTheme.colorScheme
    return if (isAlways && isInDark) {
        when (this) {
            colorScheme.primary -> colorScheme.onPrimary
            colorScheme.secondary -> colorScheme.onSecondary
            colorScheme.tertiary -> colorScheme.onTertiary
            colorScheme.background -> colorScheme.onBackground
            colorScheme.error -> colorScheme.onError
            colorScheme.surface -> colorScheme.onSurface
            colorScheme.surfaceVariant -> colorScheme.onSurfaceVariant
            colorScheme.primaryContainer -> colorScheme.onPrimaryContainer
            colorScheme.secondaryContainer -> colorScheme.onSecondaryContainer
            colorScheme.tertiaryContainer -> colorScheme.onTertiaryContainer
            colorScheme.errorContainer -> colorScheme.onErrorContainer
            colorScheme.inverseSurface -> colorScheme.inverseOnSurface

            colorScheme.onPrimary -> colorScheme.primary
            colorScheme.onSecondary -> colorScheme.secondary
            colorScheme.onTertiary -> colorScheme.tertiary
            colorScheme.onBackground -> colorScheme.background
            colorScheme.onError -> colorScheme.error
            colorScheme.onSurface -> colorScheme.surface
            colorScheme.onSurfaceVariant -> colorScheme.surfaceVariant
            colorScheme.onPrimaryContainer -> colorScheme.primaryContainer
            colorScheme.onSecondaryContainer -> colorScheme.secondaryContainer
            colorScheme.onTertiaryContainer -> colorScheme.tertiaryContainer
            colorScheme.onErrorContainer -> colorScheme.errorContainer
            colorScheme.inverseOnSurface -> colorScheme.inverseSurface

            else -> Color.Unspecified
        }
    } else {
        this
    }
}


/** Returns a lighter version of this color. */
fun Color.lighter(diff: Float): Color {
    return interpolateTowards(Color.White, 1.0f - diff)
}

/** Returns a darker version of this color. */
fun Color.darker(@FloatRange(from = 0.0, to = 1.0) diff: Float): Color {
    return interpolateTowards(Color.Black, 1.0f - diff)
}

/**
 * Darkens the color by the specified factor, where 0.0 is the darkest and 1.0 is the original color.
 */
fun Color.darkenSelf(@FloatRange(from = 0.0, to = 1.0) factor: Float = 1f): Color =
    copy(
        red = red * factor,
        green = green * factor,
        blue = blue * factor,
        alpha = alpha,
    )

fun ColorScheme.highlightColorFor(mainColor: Color) = when (mainColor) {
    primary -> primaryContainer
    secondary -> secondaryContainer
    tertiary -> tertiaryContainer
    background -> surfaceContainer
    error -> errorContainer
    primaryContainer -> primary
    secondaryContainer -> secondary
    tertiaryContainer -> tertiary
    errorContainer -> error
    inverseSurface -> surfaceContainer
    surface -> surfaceContainer
    surfaceVariant -> surface
    surfaceBright -> surfaceVariant
    surfaceContainer -> surfaceContainerHighest
    surfaceContainerHigh -> surfaceContainerLow
    surfaceContainerHighest -> surfaceContainer
    surfaceContainerLow -> surfaceContainerHigh
    surfaceContainerLowest -> surfaceVariant
    else -> Color.Unspecified
}


@Composable
fun ColorScheme.surfaceColorAtElevation(
    elevation: Dp,
    color: Color = surface,
): Color = remember(this, elevation, color) { color.atElevation(surfaceTint, elevation) }
